#include "PrecompiledHeader_Reborn.h"
#include "CelestialPlanet.h"

/*--------------------------------------------------------------------------*/

glm::mat4 CelestialPlanet::farRotMatrix		= glm::mat4();
glm::mat4 CelestialPlanet::leftRotMatrix	= glm::rotate(glm::mat4(), 90.f,	glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 CelestialPlanet::nearRotMatrix	= glm::rotate(glm::mat4(), 180.f,	glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 CelestialPlanet::rightRotMatrix	= glm::rotate(glm::mat4(), 270.f,	glm::vec3(0.0f, 1.0f, 0.0f));

glm::mat4 CelestialPlanet::topRotMatrix		= glm::rotate(glm::mat4(), 90.f,	glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 CelestialPlanet::bottomRotMatrix	= glm::rotate(glm::mat4(), 270.f,	glm::vec3(1.0f, 0.0f, 0.0f));

/*--------------------------------------------------------------------------*/

CelestialPlanet::CelestialPlanet(const NodeCreation& nc) :
	ICelestial(nc, ECelestialType::Planet)
{
	RenderTaskDesc desc;
	desc.m_material = NULL;
	desc.m_argument = 10;
	desc.m_flags = ERenderTaskFlag::UserInterface | ERenderTaskFlag::NoMaterial | ERenderTaskFlag::Transparent; //atmosfera
	desc.m_priority = 0;
	desc.m_object = this;
	this->AddRenderTask(desc);

	desc.m_argument = 12;
	desc.m_flags = ERenderTaskFlag::UserInterface | ERenderTaskFlag::NoMaterial;
	this->AddRenderTask(desc);
	/*--------------------------------------------------------------------------*/

	this->GenerateContent();

	this->SetOcclusionTestEnabled(true);
}

/*--------------------------------------------------------------------------*/

CelestialPlanet::~CelestialPlanet()
{
	this->DestroyContent();
}

/*--------------------------------------------------------------------------*/

void CelestialPlanet::RenderableBinder(uint treeCalcFlags)
{
	this->BindRenderTasks(this->GetSceneInstance()->GetRenderPhase());
}

/*--------------------------------------------------------------------------*/

void CelestialPlanet::RenderableHelper(const uint i, ERenderQuality::Type quality, uint pendingInstances)
{
	switch (i)
	{
	case 10:
	{
#ifdef VL_RENDER_STATISTICS
		g_Hud->GetStatisticsManager()->IncrementStatistic("CelestialPlanet");
#endif

		if (!g_Keyboard->GetKeyState(EKey::k_lalt))
		{
			if (this->m_atmosphere)
			{
				this->m_atmosphere->Draw(EAtmosphere::Outer, this->GetSceneInstance()->GetCamera()->GetPosition(), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), 20000.0f, this->m_planetRadius, this->m_atmosphereRadius);
			}
		}

		break;
	}

	case 12:
	{
		float r = this->GetRadius();

		if (g_Keyboard->GetKeyState(EKey::k_lalt))
		{
			g_Renderer->DrawLine3D(glm::vec3(-r, 0.0f, 0.0f), glm::vec3(r, 0.0f, 0.0f), NULL, Color::Pink);
			g_Renderer->DrawLine3D(glm::vec3(0.0f, -r, 0.0f), glm::vec3(0.0f, r, 0.0f), NULL, Color::Pink);
			g_Renderer->DrawLine3D(glm::vec3(0.0f, 0.0f, -r), glm::vec3(0.0f, 0.0f, r), NULL, Color::Pink);
		}

		break;
	}
	}
}

/*--------------------------------------------------------------------------*/

void CelestialPlanet::GenerateContent()
{
	this->m_planetRadius = 8000.0f;
	this->m_atmosphereRadius = this->m_planetRadius * 1.025f;
	this->m_maxHeight = this->m_planetRadius * 0.01f;

	this->m_smallScale.SetFrequency(1.0);
	this->m_smallScale.SetPersistence(0.5f);
	this->m_smallScale.SetLacunarity(4.0);
	this->m_smallScale.SetOctaveCount(8.0f);

	this->m_mediumScale.SetFrequency(1.5);
	this->m_mediumScale.SetPersistence(0.9f);
	this->m_mediumScale.SetLacunarity(2.0);
	this->m_mediumScale.SetOctaveCount(4.0f);

	this->m_largeScale.SetFrequency(2.0f);		//czestotlisoc?
	this->m_largeScale.SetPersistence(0.5f);	//zmiennosc?
	this->m_largeScale.SetLacunarity(2.0f);
	this->m_largeScale.SetOctaveCount(4.0f);



	NodeCreation nc;
	nc.m_flags = 0;
	nc.m_parent = this;
	nc.m_scene = this->GetSceneInstance();

	QuadCreation qc;
	qc.m_quadPosition = EQuadPosition::Ignore;
	qc.m_location = glm::vec2(0.0f, 0.0f);
	qc.m_size = glm::vec2(2.0f, 2.0f);
	qc.m_planet = this;

	this->m_quads = new CelestialPlanetQuad*[6];

	for (uint i = 0; i < 6; ++i)
	{
		qc.m_quadLocation = (EQuadLocation::Enum) i;
		this->m_quads[i] = new CelestialPlanetQuad(nc, qc);
	}

	this->m_atmosphere - new Atmosphere(this);

	this->SetRadius(this->m_planetRadius * 2.0f);
	this->SetScale(this->m_atmosphereRadius);
}

/*--------------------------------------------------------------------------*/

void CelestialPlanet::DestroyContent()
{
	for (uint i = 0; i < 6; ++i)
	{
		delete this->m_quads[i];
	}
}

/*--------------------------------------------------------------------------*/

void CelestialPlanet::DoMovement()
{
	float time = g_Sdl->GetTicks() * 0.0001f / this->GetYearLength();

	float x = std::sinf(time) * this->GetOrbit();
	float y = std::cosf(time) * this->GetOrbit();
	float z = 0.0f;

	this->SetPosition(glm::vec3(x, y, z));
}

/*--------------------------------------------------------------------------*/

float CelestialPlanet::GetPlanetRadius()
{
	return this->m_planetRadius;
}

/*--------------------------------------------------------------------------*/

float CelestialPlanet::GetAtmosphereRadius()
{
	return this->m_atmosphereRadius;
}

/*--------------------------------------------------------------------------*/

static const glm::mat4& GetRotMatrix(EQuadLocation::Enum location)
{
	switch (location)
	{
	case EQuadLocation::Far:
	{
		return CelestialPlanet::farRotMatrix;
	}

	case EQuadLocation::Near:
	{
		return CelestialPlanet::nearRotMatrix;
	}

	case EQuadLocation::Left:
	{
		return CelestialPlanet::leftRotMatrix;
	}

	case EQuadLocation::Right:
	{
		return CelestialPlanet::rightRotMatrix;
	}

	case EQuadLocation::Top:
	{
		return CelestialPlanet::topRotMatrix;
	}

	case EQuadLocation::Bottom:
	{
		return CelestialPlanet::bottomRotMatrix;
	}

	default:
	{
		AssertLogC(true, "Unknown EQuadLocation");
	}
	}
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::Rotate(const glm::vec3& pos3d, EQuadLocation::Enum location)
{
	glm::vec4 normalized = glm::normalize(glm::vec4(pos3d.x, pos3d.y, pos3d.z, 0.0f));

	switch (location)
	{
	case EQuadLocation::Far:
	{
		glm::vec4 vvv = farRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	case EQuadLocation::Near:
	{
		glm::vec4 vvv = nearRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	case EQuadLocation::Left:
	{
		glm::vec4 vvv = leftRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	case EQuadLocation::Right:
	{
		glm::vec4 vvv = rightRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	case EQuadLocation::Top:
	{
		glm::vec4 vvv = topRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	case EQuadLocation::Bottom:
	{
		glm::vec4 vvv = bottomRotMatrix * normalized;
		return glm::vec3(vvv.x, vvv.y, vvv.z);
	}

	default:
	{
		AssertLogC(true, "Unknown EQuadLocation");
	}
	}

}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::Rotate(const glm::vec2& pos2d, EQuadLocation::Enum location)
{
	return this->Rotate(glm::vec3(pos2d.x, pos2d.y, 1.0f), location);
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::RotateN(const glm::vec2& pos2d, EQuadLocation::Enum location)
{
	return glm::normalize(this->Rotate(pos2d, location));
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::RotateN(const glm::vec3& pos3d, EQuadLocation::Enum location)
{
	return glm::normalize(this->Rotate(pos3d, location));
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::TranslationVector(EQuadLocation::Enum location)
{
	glm::vec4 v = (GetRotMatrix(location) * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f)) * this->m_planetRadius;
	return glm::vec3(v.x, v.y, v.z);
}

/*--------------------------------------------------------------------------*/

#define WEIGHT_S 1.0f
#define WEIGHT_M 80.0f
#define WEIGHT_L 100.0f
#define INV_WEIGHT_SUM (1.0f / (WEIGHT_S + WEIGHT_M + WEIGHT_L))

float CelestialPlanet::Height(const glm::vec3& nvec)
{
	float value = 0.0f;

	glm::vec3 p = nvec * 100.0f;
	glm::vec3 m = nvec * 4.0f;
	glm::vec3 r = nvec;

	float s = WEIGHT_S * this->m_smallScale.GetValue(p.x, p.y, p.z);
	float n = WEIGHT_M * this->m_mediumScale.GetValue(m.x, m.y, m.z);
	float l = WEIGHT_L * this->m_largeScale.GetValue(r.x, r.y, r.z);

	value += s;
	value += abs(n);
	value += l * abs(n * s * 0.001f);

	float h = 1.0f - (value * INV_WEIGHT_SUM);

	h = abs(h - 0.2f) * 1.2f;
	h = h * h;

	h = abs(h - 0.2f) * 1.2f;

	h *= n / 80.0f;

	return (h * this->m_maxHeight);
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::Normal(const glm::vec2& pos2d, EQuadLocation::Enum location)
{
	float delta = 0.001f;

	glm::vec3 points[4];
	points[0] = this->RotateN(pos2d + glm::vec2(delta, 0.0f), location);
	points[1] = this->RotateN(pos2d + glm::vec2(-delta, 0.0f), location);
	points[2] = this->RotateN(pos2d + glm::vec2(0.0f, delta), location);
	points[3] = this->RotateN(pos2d + glm::vec2(0.0f, -delta), location);

	for (uint i = 0; i < 4; ++i)
	{
		points[i] = this->Position(points[i]);
	}

	return glm::normalize(glm::cross((points[0] - points[1]) , (points[2] - points[3])));
}

/*--------------------------------------------------------------------------*/

float CelestialPlanet::GetMaxHeight()
{
	return this->m_maxHeight;
}

/*--------------------------------------------------------------------------*/

glm::vec3 CelestialPlanet::Position(const glm::vec3& nvec)
{
	float height = this->Height(nvec);

	return nvec * (this->m_planetRadius + height);
}

/*--------------------------------------------------------------------------*/
